#ifndef _AST_HPP
#define	_AST_HPP

#include <list>
#include <string>
#include <vector>
#include <cassert>

#define LINE_OFFSET 140

namespace ast
{

using std::string;
using std::vector;
using std::list;

class Node;
class Location;
class Visitor;

class Program;
class DataSection;
class TextSection;
class Directive;
class Instruction;
class Operand;

typedef string Label;
typedef vector<Label*> LabelList;
typedef vector<int> IntegerList;

typedef list<DataSection*> DataSectionList;
typedef list<TextSection*> TextSectionList;
typedef vector<Directive*> DirectiveList;
typedef vector<Instruction*> InstructionList;


template<typename T>
void free_vector(vector<T*>* v)
{
  for (uint i = 0; i < v->size(); ++i)
    delete v->at(i);

  delete v;
}


template<typename T>
void free_list(list<T*>* l)
{
  for (typename list<T*>::iterator it = l->begin(); it != l->end(); ++it)
    delete *it;

  delete l;
}


template<typename T>
vector<T*>* clone_vector(vector<T*>* v)
{
  vector<T*>* copy = new vector<T*>(v->size());
  
  for (uint i = 0; i < v->size(); ++i)
    copy->at(i) = v->at(i)->clone();

  return copy;
}


template<typename T>
list<T*>* clone_list(list<T*>* l)
{
  list<T*>* copy = new list<T*>();

  for (typename list<T*>::iterator it = l->begin(); it != l->end(); ++it)
    copy->push_back((*it)->clone());

  return copy;
}



class Location {
public:
  int first_line;
  int first_column;
  int last_line;
  int last_column;
  const char* filename;
};


class Node {
public:
  virtual ~Node();
  virtual Node* clone() = 0;
  virtual void accept(Visitor*) = 0;

  Location location;
};


class Visitor {
public:
  virtual void visit(Program&);
  virtual void visit(DataSection&);
  virtual void visit(Directive&);
  virtual void visit(TextSection&);
  virtual void visit(Instruction&);
  virtual void visit(Operand&);
};


struct Program : public Node {
  Program();
  Program(DataSectionList* ds, TextSectionList* ts);
  ~Program();
  
  Program* clone();
  virtual void accept(Visitor* v);

  DataSectionList* dataSections;
  TextSectionList* textSections;
};


struct DataSection : public Node {
  DataSection();
  DataSection(DirectiveList* dl);
  ~DataSection();

  DataSection* clone();
  virtual void accept(Visitor* v);
  
  DirectiveList* directives;
};


struct Directive : public Node {
  enum Type {
    Size,
    StringLiteral,
    IntList,
    Label,
    Linkage
  };

  Directive(int dir_type);
  Directive(int dir_type, Type arg_type);
  ~Directive();

  Directive* clone();
  virtual void accept(Visitor* v);
  
  int dirType;
  
  union {
    size_t size;
    string* str;
    IntegerList* intList;
  } arg;

  Type argType;

  LabelList* labels;
};


struct TextSection : public Node {
  TextSection(InstructionList* il);
  ~TextSection();

  TextSection* clone();
  virtual void accept(Visitor* v);

  InstructionList* instructions;
};


struct Operand : public Node {
  // See: RISCO instruction format manual
  enum Type {
    Register,      // Rx: normal register (can be R0)
    ZeroRegister,  // R0: special encoding saves space for constant
    ConstantP,     // Kpe: 11 bits, sign extended
    ConstantL,     // Kgl: 17 bits, sign extended
    ConstantH,     // Kgh: 17 bits, special extension method
    LabelC,        // Label representing a numeric constant
    LabelH         // Label representing the upper bits of a numeric constant
  };

  Operand(Type t);
  Operand(Type t, int val);
  Operand(Type t, Label* l);

  Operand* clone();

  static Operand* getR0();
  static Operand* getPC();
  static Operand* getSP();

  bool isConstant();
  bool isConstantH();
  bool isConstantL();
  bool isLabel();
  bool isLabelH();
  bool isH();

  virtual void accept(Visitor* v);

  Type type;

  union {
    int value;
    Label* str;
  } arg;
};


struct Instruction : public Node {
  Instruction(int op);

  Instruction(int format, Operand* a, Operand* b, Operand* c);

  Instruction(Operand* a, Operand* b, Operand* c);

  Instruction(LabelList* ll,
              int op, int format,
              Operand* a, Operand* b, Operand* c);

  ~Instruction();

  void init();

  Instruction* clone();
  virtual void accept(Visitor* v);

  LabelList* labels;
  
  int opcode;
  bool apsBit;
  int operand_format;

  Operand *op1, *op2, *op3;
};


}


#endif
